package httpsrv

import (
	"bufio"
	"bytes"
	"chatgpt/helper"
	"chatgpt/logq"
	"encoding/json"
	"errors"
	"github.com/slclub/go-tips"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

/**
 *  http GET 请求数据 仅支持json格式的
 * 	resp_data := make(map[string]interface{}) // resp_data 也可以是结构体
 *	err := Get("http://127.0.0.1:8080/info", &resp_data)
 */
func Get(address string, dest interface{}) error {
	resp, err := request("GET", address, nil)
	if err != nil {
		logq.Error("[GET][REQ]" + err.Error())
	}
	err = json.Unmarshal(resp, dest)
	if err != nil {
		logq.Error("[GET][Unmarshal]" + err.Error())
	}
	return err
}

func Post(address string, data map[string]interface{}, dest interface{}) error {
	resp, err := request("POST", address, data)
	if err != nil {
		logq.Error("[POST][REQ]" + err.Error())
	}
	err = json.Unmarshal(resp, dest)
	if err != nil {
		logq.Error("[POST][Unmarshal]" + err.Error())
	}
	return err
}

func PostForm(address string, data map[string]interface{}, dest interface{}) error {
	resp, err := request("FORM", address, data)
	if err != nil {
		logq.Error("[POSTFORM][REQ]" + err.Error())
	}
	err = json.Unmarshal(resp, dest)
	if err != nil {
		logq.Error("[POSTFORM][Unmarshal]" + err.Error())
	}
	return err
}

func request(method string, address string, data map[string]interface{}) ([]byte, error) {

	method = strings.ToUpper(method)
	var resp *http.Response
	var err error
	switch method {
	case "GET":
		resp, err = http.Get(address)
	case "POST":
		data_code, _ := json.Marshal(data)
		resp, err = http.Post(address, "application/json", bytes.NewBuffer(data_code))
	case "FORM":
		form_data := make(url.Values)
		for key, value := range data {
			form_data[string(key)] = []string{value.(string)}
		}
		resp, err = http.PostForm(address, form_data)
	}

	if err != nil {
		logq.Error("[HTTP][method:%s][url:%s][param:%s]error:", method, address, data, err.Error())
		return []byte{}, nil
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		logq.Error("[HTTP][BODY][READ]%s", err.Error())
	}
	return body, err
}

func PostStream(w http.ResponseWriter, address string, data map[string]any, dest io.Writer) error {
	return RequestStream(w, address, data, dest, nil)
}

func RequestStream(w http.ResponseWriter, address string, data map[string]any, dest io.Writer, head func(r *http.Request)) error {
	data_code, _ := json.Marshal(data)
	client := http.Client{}
	method := "POST"
	if val, ok := data["METHOD"]; ok {
		method = tips.String(val)
	}

	reqest, err := http.NewRequest(method, address, bytes.NewBuffer(data_code))
	reqest.Header.Set("Content-Type", "application/json")
	reqest.Header.Set("Accept", "*/*")
	reqest.Header.Set("Cache-Control", "no-cache")
	reqest.Header.Set("Connection", "keep-alive")
	if head != nil {
		head(reqest)
	}
	resp, err := client.Do(reqest)
	//resp, err := http.Post(address, "application/json", bytes.NewBuffer(data_code))

	if err != nil {
		logq.Error("[POST][REQ]" + err.Error())
		return err
	}
	if w != nil {
		w.Header().Set("Content-Type", "text/event-stream; charset=utf-8")
		w.Header().Set("Cache-Control", "no-cache")
		w.Header().Set("Connection", "keep-alive")
		w.Header().Set("Keep-Alive", "timeout=5")
		w.Header().Set("Transfer-Encoding", "chunked")
	}
	defer resp.Body.Close()
	buff := bufio.NewReader(resp.Body)

	//scanner := bufio.NewScanner(resp.Body)
	//scanner.Split(bufio.ScanWords)
	rtn_str := []byte{}
	if resp.StatusCode == http.StatusOK {
		for {
			evb, _, err := buff.ReadRune()
			if err != nil || err == io.EOF {
				break
			}
			event_data := string(evb)
			//event_data := scanner.Text()
			//if event_data == "" {
			//	continue
			//}
			rtn_str = append(rtn_str, event_data[:]...)

			// 流式输出
			if w != nil {
				writeWithResponse(w, []byte(event_data), dest)
			}
		}
	} else {
		rtn_str = []byte(resp.Status)
		err = errors.New(resp.Status)
	}

	if dest != nil {
		dest.Write(rtn_str)
	}
	flush(w)

	return err
}

func writeWithResponse(w http.ResponseWriter, b []byte, dest io.Writer) {
	if tips.IsNil(dest) {
		w.Write(b)
	} else {
		desta, ok := dest.(helper.Writer)
		if ok {
			desta.WriteAppend(b)
			n := tips.Rand.Intn(10)
			if n >= 5 {
				return
			}
		} else {
			dest.Write(b)
		}
		b, _ := json.Marshal(dest)
		b = append(b, '\n')
		w.Write(b)
		dest.Write(nil)
	}
	flush(w)
}

func flush(w http.ResponseWriter) {
	flusher, ok := w.(http.Flusher)
	if ok {
		flusher.Flush()
	}
}
